package com.jiao.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jiao.domain.DTO.RegisterDto;
import com.jiao.domain.LoginUser;
import com.jiao.domain.Result;
import com.jiao.domain.VO.UserInfoVo;
import com.jiao.domain.entity.Follow;
import com.jiao.domain.entity.LikeArticle;
import com.jiao.domain.entity.SysUser;
import com.jiao.enums.ExceptionCodeEnum;
import com.jiao.mapper.FollowMapper;
import com.jiao.mapper.SysUserMapper;
import com.jiao.service.SysUserService;
import com.jiao.utils.BeanCopyUtils;
import com.jiao.utils.RedisCache;
import com.jiao.utils.RedisKeyUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiao.utils.SecurityUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 用户表(SysUser)表服务实现类
 *
 * @author makejava
 * @since 2022-02-27 15:20:09
 */
@Service("sysUserService")
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    @Autowired
    SysUserMapper sysUserMapper;
    @Autowired
    RedisCache redisCache;
    //RedisTemplate<String,Long> redisTemplate; 这个redisTemplate是没有的，需要自定义实现
    @Autowired
    RedisTemplate<String,Object> redisTemplate;
    @Autowired
    FollowMapper followMapper;

    //其实也就是普通的查询功能，正常来说哈，像刚登陆时显示一些基础数据。
    //而点击个人中心按钮，则会调用/user/userinfo接口，返回个人完整的信息数据，比如个人签名，邮行，手机号等。
    @Override
    public Result<UserInfoVo> userInfo() {
        //获取token 解析获取userid
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //获取userid
        SysUser sysUser = loginUser.getSysUser();
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(sysUser, UserInfoVo.class);

        return Result.success(userInfoVo);
    }

    @Override
    public Result<Void> updateUserInfo(UserInfoVo userInfoVo) {
        //获取token 解析获取userid
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getSysUser().getId();
        //对象封装
        SysUser sysUser = BeanCopyUtils.copyBean(userInfoVo, SysUser.class);
        sysUser.setId(userId);
        if(sysUserMapper.updateById(sysUser) != 1){
            return Result.error(ExceptionCodeEnum.UPDATE_USERINFO_ERROR);
        }
        //更改缓存
        loginUser.getSysUser().setNickName(userInfoVo.getNickName());
        loginUser.getSysUser().setAvatar(userInfoVo.getAvatar());
        loginUser.getSysUser().setSex(userInfoVo.getSex());
        loginUser.getSysUser().setEmail(userInfoVo.getEmail());
        //把用户信息存入redis。set命令，设置key-value，如果键值已存在，则会覆盖value值。
        redisCache.setCacheObject("blogLogin:"+userId,loginUser);
        return Result.success();
    }

    @Override
    public Result<Boolean> register(RegisterDto registerDto) {
        return null;
    }

    /*
    * 实现关注和粉丝的功能，使用redis
    * */
    @Override
    public Result<Boolean> follow(Long followerId) {
        //获取当前用户id
        Long userId = SecurityUtils.getLoginId();

        return Result.success(followClick(followerId, userId));
    }

    /*
     * 实现关注和粉丝的功能，使用redis
     * 其实也可以只添加关注人的redis，这样是不行的，容易造成数据不一致性，只能都写。
     * */
    public Boolean followClick(Long followerId,Long sysUserId) {
        //粉丝人集合
        String followeeListKey = RedisKeyUtil.getFolloweeListKey(followerId);
        //关注人集合
        String followerListKey = RedisKeyUtil.getFollowerListKey(sysUserId);

        Map<String,String> followsMap = new HashMap<>();

        //判断redis有无该关注人的粉丝数据，如果不存在，则查找数据库中所有关于该关注人的粉丝数据
        if(Objects.equals(redisTemplate.hasKey(followeeListKey),false)){
            List<Follow> followees = followMapper.selectList(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowerId, followerId));
            //数据库中粉丝数据不为空，那就提取数据库中数据放入缓存中
            if(!followees.isEmpty()){
                followees.forEach( follow -> followsMap.put(String.valueOf(follow.getFolloweeId()),follow.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
                redisTemplate.opsForHash().putAll(followeeListKey,followsMap);
            }
        }

        followsMap.clear();
        //判断redis有无该用户的关注人集合，如果不存在，则查找数据库中所有关于该用户的关注人数据
        if(Objects.equals(redisTemplate.hasKey(followerListKey),false)){
            List<Follow> followers = followMapper.selectList(new LambdaQueryWrapper<Follow>().eq(Follow::getFolloweeId, sysUserId));
            //数据库中粉丝数据不为空，那就提取数据库中数据放入缓存中
            if(!followers.isEmpty()){
                followers.forEach( follow -> followsMap.put(String.valueOf(follow.getFollowerId()),follow.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
                redisTemplate.opsForHash().putAll(followerListKey,followsMap);
            }
        }

        Boolean isFollow = isFollow(followerId, sysUserId);
        String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        // 开启redis事务
        redisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(@NotNull RedisOperations operations) throws DataAccessException {
                operations.multi();
                if(Objects.equals(isFollow,true)){
                    redisTemplate.opsForHash().delete(followerListKey,followerId);
                    redisTemplate.opsForHash().delete(followeeListKey,sysUserId);
                }else{
                    redisTemplate.opsForHash().put(followerListKey,followerId,nowTime);
                    redisTemplate.opsForHash().put(followeeListKey,sysUserId,nowTime);
                }
                return operations.exec();
            }
        });

        return !isFollow;
    }

    /*
     * 判断 followeeId的粉丝集合有无 sysUserId
     * */
    public Boolean isFollow(Long followerId,Long sysUserId) {
        //粉丝人集合
        String followeeListKey = RedisKeyUtil.getFolloweeListKey(followerId);

        return redisTemplate.opsForHash().hasKey(followeeListKey,sysUserId);
    }

}
